using System;
using System.Xml;
using System.Text.RegularExpressions;

namespace ServiceModel.Syndication.LEAP2A {
    /// <summary>
    /// Used to represent time information connected with the subject matter of a record,
    /// see http://wiki.cetis.ac.uk/LEAP2A_literals#activetime
    /// </summary>
    public class LeapTimeSpan {
        /// <summary>
        /// Gets or sets the value of the date.
        /// </summary>
        /// <value>The date value.</value>
        public TimeSpan Value {
            get;
            set;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapDate"/> class.
        /// </summary>
        public LeapTimeSpan() {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapDate"/> class,
        /// with a type and value of the date.
        /// </summary>
        /// <param name="timespan">The timespan.</param>
        public LeapTimeSpan(TimeSpan timespan) {
            this.Value = timespan;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapTimeSpan"/> class.
        /// </summary>
        /// <param name="hours">The number of hours.</param>
        /// <param name="minutes">The number of minutes.</param>
        /// <param name="seconds">The nubmer of seconds.</param>
        public LeapTimeSpan(int hours, int minutes, int seconds) {
            this.Value = new TimeSpan(hours, minutes, seconds);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapTimeSpan"/> class.
        /// </summary>
        /// <param name="hours">The number of hours.</param>
        /// <param name="minutes">The number of minutes.</param>
        public LeapTimeSpan(int hours, int minutes) {
            this.Value = new TimeSpan(hours, minutes, 0);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LeapTimeSpan"/> class.
        /// </summary>
        /// <param name="minutes">The number of minutes.</param>
        public LeapTimeSpan(int minutes) {
            this.Value = new TimeSpan(0, minutes, 0);
        }

        /// <summary>
        /// Writes an ISO 8601 duration to the Atom feed, units above days are not used.
        /// </summary>
        /// <param name="writer">The writer.</param>
        public void WriteTo(XmlWriter writer, LeapFeed feed) {
            string duration = "P";

            if (Value.Days > 0) {
                duration += Value.Days + "D";
            }

            string timePart = "";

            if (Value.Hours > 0) {
                timePart += Value.Hours + "H";
            }

            if (Value.Minutes > 0) {
                timePart += Value.Minutes + "M";
            }

            if (Value.Milliseconds > 0) {
                timePart += Value.Seconds + "." + (Value.Milliseconds / 1000) + "S";
            } else if (Value.Seconds > 0) {
                timePart += Value.Seconds + "S";
            }

            if (timePart != "") {
                duration += "T" + timePart;
            }

            writer.WriteElementString(
                feed.LeapPredicatesNamespacePrefix,
                "activetime",
                feed.LeapPredicatesNamespace,
                duration);
        }

        /// <summary>
        /// Tries to parse and LeapTimeSpan from an ISO 8601 duration in an Atom feed.
        /// </summary>
        /// <param name="reader">The reader.</param>
        /// <param name="publishDate">The publish date.</param>
        /// <returns></returns>
        public bool TryParseTimeSpan(XmlReader reader, DateTime publishDate) {
            if (reader.LocalName == "activetime") {
                string time = LeapItem.GetTextContent(reader);

                if (!string.IsNullOrEmpty(time)) {
                    Regex regex = new Regex("P(?:([0-9]*)Y)?(?:([0-9]*)M)?(?:([0-9]*)D)?(?:T(?:([0-9]*)H)?(?:([0-9]*)M)?(?:([0-9\\.]*)S)?)?");

                    Match match = regex.Match(time);

                    if (match.Success) {
                        int years, months, days, hours, minutes;
                        decimal seconds;

                        int.TryParse(match.Groups[1].Value, out years);
                        int.TryParse(match.Groups[2].Value, out months);
                        int.TryParse(match.Groups[3].Value, out days);
                        int.TryParse(match.Groups[4].Value, out hours);
                        int.TryParse(match.Groups[5].Value, out minutes);
                        decimal.TryParse(match.Groups[6].Value, out seconds);

                        DateTime baseTime = publishDate;
                        DateTime fullTime = baseTime;

                        int wholeSeconds = (int)Math.Floor(seconds);
                        int milliseconds = Convert.ToInt32((seconds - wholeSeconds) * 1000);

                        fullTime = fullTime.AddYears(years);
                        fullTime = fullTime.AddMonths(months);
                        fullTime = fullTime.AddDays(days);
                        fullTime = fullTime.AddHours(hours);
                        fullTime = fullTime.AddMinutes(minutes);
                        fullTime = fullTime.AddSeconds(wholeSeconds);
                        fullTime = fullTime.AddMilliseconds(milliseconds);

                        Value = fullTime.Subtract(baseTime);

                        if (Value.Ticks > 0) {
                            return true;
                        }

                        return false;
                    }
                }
            }

            return false;
        }
    }
}
